package com.platform.helper.office.excel;

import com.platform.helper.bean.MapperUtil;
import com.platform.helper.date.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * describe:Excel读取协助处理对象
 * 建造者模式
 * @author rainly
 * @date 2020/4/2
 * @time 4:53 下午
 */
@Slf4j
public class ExcelReadBean {
    /**
     * 开始页数
     */
    private Integer beginSheetNum;
    /**
     * 结束页数
     */
    private Integer endSheetNum;
    /**
     * 开始行数
     */
    private Integer beginRowNum;
    /**
     * 结束行数
     */
    private Integer endRowNum;
    /**
     * 开始列数
     */
    private Integer beginColumnNum;
    /**
     * 结束列数
     */
    private Integer endColumnNum;
    /**
     * 数据方向
     */
    private DataVector dataVector;
    /**
     * 映射的key
     */
    private List<String> keys;
    /**
     * 排除映射的key
     */
    private List<String> exclusionKeys;
    /**
     * 映射转成的对象class
     */
    private Class<?> clazz;

    private ExcelReadBean(){
    }

    /**
     * 默认设置
     */
    private void defaultSetting(){
        this.beginSheetNum = 0;
        this.beginRowNum = 0;
        this.beginColumnNum = 0;
        this.dataVector = DataVector.ROW;
    }

    /**
     * 映射的key的构造函数
     * @param keys 映射的key
     */
    public ExcelReadBean(List<String> keys){
        defaultSetting();
        this.keys = keys;
    }

    /**
     * 映射对象的的构造函数
     * @param clazz 映射对象
     */
    public ExcelReadBean(Class<?> clazz){
        defaultSetting();
        setClazz(clazz);
    }

    public List<String> getExclusionKeys() {
        return exclusionKeys;
    }

    /**
     * 设置需要排除的key
     * @param exclusionKeys 排除映射的key
     * @return ExcelReadBean
     */
    public ExcelReadBean setExclusionKeys(List<String> exclusionKeys) {
        this.exclusionKeys = exclusionKeys;
        return this;
    }

    /**
     * 获得开始页数
     * @return Integer
     */
    public Integer getBeginSheetNum() {
        return beginSheetNum;
    }

    /**
     * 设置开始页数
     * @param beginSheetNum 开始页数
     * @return Integer
     */
    public ExcelReadBean setBeginSheetNum(Integer beginSheetNum) {
        checkSheetNum(beginSheetNum);
        return this;
    }

    public Integer getEndSheetNum() {
        return endSheetNum;
    }

    public ExcelReadBean setEndSheetNum(Integer endSheetNum) {
        if (endSheetNum < 1){
            this.endSheetNum = 1;
        }else{
            this.endSheetNum = endSheetNum;
        }
        return this;
    }

    public Integer getBeginRowNum() {
        return beginRowNum;
    }

    public ExcelReadBean setBeginRowNum(Integer beginRowNum) {
        checkRowNum(beginRowNum);
        return this;
    }

    public Integer getEndRowNum() {
        return endRowNum;
    }

    public ExcelReadBean setEndRowNum(Integer endRowNum) {
        if (endRowNum < 1){
            this.endRowNum = 1;
        }else{
            this.endRowNum = endRowNum;
        }
        return this;
    }

    public Integer getBeginColumnNum() {
        return beginColumnNum;
    }

    public ExcelReadBean setBeginColumnNum(Integer beginColumnNum) {
        checkColumnNum(beginColumnNum);
        return this;
    }

    public Integer getEndColumnNum() {
        return endColumnNum;
    }

    public ExcelReadBean setEndColumnNum(Integer endColumnNum) {
        if (endColumnNum < 1){
            this.endColumnNum = 1;
        }else{
            this.endColumnNum = endColumnNum;
        }
        return this;
    }

    public DataVector getDataVector() {
        return dataVector;
    }

    /**
     * 设置数据方向
     * @param dataVector 数据方向
     * @return ExcelReadBean
     */
    public ExcelReadBean setDataVector(DataVector dataVector) {
        this.dataVector = dataVector;
        return this;
    }

    public List<String> getKeys() {
        return keys;
    }

    public ExcelReadBean setKeys(List<String> keys) {
        this.keys = keys;
        return this;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public void end(){
        //将需要排除的映射字段进行排除
        if (this.keys != null && this.exclusionKeys != null){
            keys.removeAll(exclusionKeys);
        }
    }


    /**
     * 通过反射获得对象的属性
     * @param clazz 要封装成的对象
     */
    public ExcelReadBean setClazz(Class<?> clazz) {
        this.clazz = clazz;

        if (!CollectionUtils.isEmpty(this.keys)){
            return this;
        }
        try {
            Class<?> c = Class.forName(clazz.getName());
            Field[] fields = c.getDeclaredFields();
            this.keys = new ArrayList<>(fields.length);
            for (Field field : fields) {
                this.keys.add(field.getName());
            }
        } catch (ClassNotFoundException e) {
            log.error("封装对象的类不存在", e);
        }
        return this;
    }

    /**
     * Map转对象
     * @param map map
     * @return Object对象
     */
    public Object mapToObject(Map<String, String> map){
        if (map == null){
            return null;
        }
        try {
            Object obj = this.clazz.newInstance();
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                if (map.containsKey(field.getName())) {
                    String val = map.get(field.getName());
                    if (StringUtils.isEmpty(val)){
                        continue;
                    }
                    switch (field.getType().getName()){
                        case "byte":
                            field.set(obj, Byte.parseByte(val));
                            break;
                        case "short":
                            field.set(obj, Short.parseShort(val));
                            break;
                        case "int":
                            field.set(obj, Integer.parseInt(val));
                            break;
                        case "long":
                            field.set(obj, Long.parseLong(val));
                            break;
                        case "float":
                            field.set(obj, Float.parseFloat(val));
                            break;
                        case "double":
                            field.set(obj, Double.parseDouble(val));
                            break;
                        case "boolean":
                            field.set(obj, Boolean.parseBoolean(val));
                            break;
                        case "java.lang.String":
                            field.set(obj, val);
                            break;
                        case "java.util.Date":
                            field.set(obj, DateUtils.formatTime(val));
                            break;
                        default:
                            log.error("无法识别的类属性的类型");
                            break;
                    }
                }
            }
            return obj;
        } catch (Exception e) {
            log.error("Map装对象异常", e);
        }
        return null;
    }

    private void checkSheetNum(int beginSheetNum){
        if(beginSheetNum <= 0){
            this.beginSheetNum = 0;
        }else{
            this.beginSheetNum = beginSheetNum - 1;
        }
    }

    private void checkRowNum(int beginRowNum){
        int maxRowNum = 1048576;
        if (beginRowNum > maxRowNum){
            this.beginRowNum = maxRowNum;
        }else if(beginSheetNum <= 0){
            this.beginRowNum = 0;
        }else{
            this.beginRowNum = beginRowNum - 1;
        }
    }

    private void checkColumnNum(int beginColumnNum){
        int maxColumnNum = 16384;
        if (beginColumnNum > maxColumnNum){
            this.beginColumnNum = maxColumnNum;
        }else if(beginColumnNum <= 0){
            this.beginColumnNum = 0;
        }else {
            this.beginColumnNum = beginColumnNum - 1;
        }
    }
}
